﻿// WebSocketTest.cpp : 定义控制台应用程序的入口点。
//

//#include "stdafx.h"

//#include <boost/algorithm/string.hpp>
#include <string>
#include <vector>
#include <list>

#include <iostream>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>


//名称与值数据对
struct NameAndValue
{
	std::string strName;
	std::string strValue;
};
// 字符串分割
int StringSplit(std::vector<std::string>& dst, const std::string& src, const std::string& separator);
//去前后空格
std::string& StringTrim(std::string &str);
//获取请求命令与参数
bool GetReqeustCommandAndParmeter(std::string strUri, std::string & strRequestOperateCommand, std::list<NameAndValue> & listRequestOperateParameter);

typedef websocketpp::server<websocketpp::config::asio> server;

using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;

// pull out the type of messages sent by our config
typedef server::message_ptr message_ptr;

bool validate(server *s, websocketpp::connection_hdl hdl) {
	//sleep(6);
	server::connection_ptr con = s->get_con_from_hdl(hdl);

	std::string res = con->get_request_body();

	std::stringstream ss;
	ss << "got HTTP request with " << res.size() << " bytes of body data.";
	return true;
}

void on_http(server* s, websocketpp::connection_hdl hdl) {
	server::connection_ptr con = s->get_con_from_hdl(hdl);

	std::string res = con->get_request_body();

	std::stringstream ss;
	ss << "got HTTP request with " << res.size() << " bytes of body data.";

	con->set_body(ss.str());
	con->set_status(websocketpp::http::status_code::ok);
}

void on_fail(server* s, websocketpp::connection_hdl hdl) {
	server::connection_ptr con = s->get_con_from_hdl(hdl);

	std::cout << "Fail handler: " << con->get_ec() << " " << con->get_ec().message() << std::endl;
}

void on_open(server* s, websocketpp::connection_hdl hdl) {
	//申请websocket upgrade成功之后，调用open_handler函数，回调on_open。
	//在这里，可以获取http请求的地址、参数信息。
	std::cout << "open handler" << std::endl;
	
	server::connection_ptr con = s->get_con_from_hdl(hdl);
	websocketpp::config::core::request_type requestClient = con->get_request();
	std::string strMethod = requestClient.get_method();		//请求方法
	std::string strUri = requestClient.get_uri();			//请求uri地址，可以解析参数
	std::string strRequestOperateCommand = "";				//操作类型
	std::list<NameAndValue> listRequestOperateParameter;	//操作参数列表
	GetReqeustCommandAndParmeter(strUri, strRequestOperateCommand, listRequestOperateParameter);
	std::cout << "command:" << strRequestOperateCommand << std::endl;
	
	
}

void on_close(websocketpp::connection_hdl hdl) {
	std::cout << "Close handler" << std::endl;
}

bool on_ping(server* s, websocketpp::connection_hdl hdl, std::string msg) {
	//m_status = "Ping";
	std::cout << "Ping handler: " << msg << std::endl;
	//server::connection_ptr con = s->get_con_from_hdl(hdl);
	////pong,原样返回
	//con->pong(msg);
	return true;
}
//
void on_pong(server* s,websocketpp::connection_hdl hdl, std::string msg) {
	//m_status = "Pong";
	
	std::cout << "Pong handler: "<< msg << std::endl;
}

// Define a callback to handle incoming messages
void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {

	if (msg->get_opcode() == websocketpp::frame::opcode::text) {
		
	}
	else {
		std::cout<< "binary data:" << websocketpp::utility::to_hex(msg->get_payload());
	}
	/*
		hdl.lock().get() 获得连接标识
		msg->get_payload() 是收到的消息内容
		msg->get_opcode() 是收到消息的类型 ，包含：文本TEXT,二进制BINARY等等
	*/
	std::cout << "on_message called with hdl: " << hdl.lock().get()
		<< " and message: " << msg->get_payload()
		<< std::endl;

	try {
		/*
			发送消息
			s->send(
			hdl, //连接
			msg->get_payload(), //消息
			msg->get_opcode());//消息类型
		*/
		s->send(hdl, msg->get_payload(), msg->get_opcode());
		std::cout << "on_message  end..." <<std::endl;
	}
	catch (websocketpp::exception const & e) {
		std::cout << "Echo failed because: "
			<< "(" << e.what() << ")" << std::endl;
	}
}


int main1() {
	server print_server;
	try {
		//在套接字初始化之后但在建立连接之前触发此挂钩。它允许在发送/接收连接之前设置任意套接字选项。
		//print_server.set_socket_init_handler
		//在建立TCP连接之后但在运行任何WebSocket握手之前的操作之前，将触发此挂钩。常见的握手前操作包括TLS握手和代理连接。
		//print_server.set_tcp_pre_init_handler
		//?
		//print_server.set_tcp_init_handler
		//?
		//print_server.set_tcp_pre_bind_handler
		//在TLS握手之前触发此挂钩，以请求使用TLS上下文。您必须返回指向已配置TLS扩展程序的指针才能继续。这提供了设置TLS设置，证书等的机会。
		//print_server.tls_init
		
		//在处理请求之后但在发送响应之前，在打开握手期间会为服务器触发此挂钩。它为程序提供了检查标头和其他连接详细信息以及接受或拒绝连接的机会。验证发生在打开或失败处理程序之前。
		//返回true接受连接，返回false拒绝连接。如果未注册任何验证处理程序，则将接受所有连接。
			//握手之后，接受连接之前。验证是否接受连接
		print_server.set_validate_handler(bind(&validate, &print_server, ::_1));
		//每个连接都会调用open或fail。永远不要都。当连接结束时，所有以打开处理程序调用开头的连接也将具有匹配的关闭处理程序调用。
		/*每次成功的WebSocket连接尝试都会调用open处理程序一次。对于每次WebSocket连接尝试，
		失败处理程序或打开处理程序都会被调用。没有尝试将连接升级到WebSocket协议的HTTP连接将触发HTTP处理程序，
		而不是fail / open。*/
		print_server.set_open_handler(bind(&on_open, &print_server, ::_1));
		//每个连接都会调用open或fail。永远不要都。失败的连接永远不会调用关闭处理程序。
		print_server.set_fail_handler(bind(&on_fail, &print_server, ::_1));
		//对于需要打开的每个连接，将只一次调用一次Close。连接失败不会调用关闭。
		print_server.set_close_handler(bind(&on_close, ::_1));

		// Set logging settings
		print_server.set_access_channels(websocketpp::log::alevel::all);
		print_server.set_error_channels(websocketpp::log::elevel::all);
		//print_server.clear_access_channels(websocketpp::log::alevel::frame_payload);

		// Register our message handler
		//适用于所有非控制消息，包括文本和二进制操作码。该message_ptr类型及其API取决于您的端点类型和配置。
		print_server.set_message_handler(bind(&on_message, &print_server, ::_1, ::_2));
		//没有尝试将连接升级到WebSocket协议的HTTP连接将触发HTTP处理程序，
		//当收到非WebSocket握手升级请求的HTTP请求时调用。允许响应常规的HTTP请求。如果未注册任何处理程序，则返回426 / Upgrade Required错误。
		print_server.set_http_handler(bind(&on_http, &print_server, ::_1));
		
		print_server.set_ping_handler(bind(&on_ping, &print_server, ::_1, ::_2));
		print_server.set_pong_handler(bind(&on_pong, &print_server, ::_1, ::_2));

		// Initialize ASIO
		print_server.init_asio();
		print_server.set_reuse_addr(true);

		// Listen on port 9100
		print_server.listen(9100);
		// Start the server accept loop
		std::cout << "启动 server" << std::endl;
		print_server.start_accept();

		// Start the ASIO io_service run loop
		print_server.run();

		//stop
		print_server.stop();
	}
	catch (websocketpp::exception const & e) {
		std::cout << e.what() << std::endl;
	}
	catch (const std::exception & e) {
		std::cout << e.what() << std::endl;
	}
	catch (...) {
		std::cout << "other exception" << std::endl;
	}
	std::cout << "init websocket server ok" << std::endl;
}


// 字符串分割
int StringSplit(std::vector<std::string>& dst, const std::string& src, const std::string& separator)
{
	if (src.empty() || separator.empty())
		return 0;

	int nCount = 0;
	std::string temp;
	size_t pos = 0, offset = 0;

	// 分割第1~n-1个
	while ((pos = src.find_first_of(separator, offset)) != std::string::npos)
	{
		temp = src.substr(offset, pos - offset);
		if (temp.length() > 0) {
			dst.push_back(temp);
			nCount++;
		}
		offset = pos + 1;
	}

	// 分割第n个
	temp = src.substr(offset, src.length() - offset);
	if (temp.length() > 0) {
		dst.push_back(temp);
		nCount++;
	}

	return nCount;
}
//去前后空格
std::string& StringTrim(std::string &str)
{
	if (str.empty()) {
		return str;
	}
	str.erase(0, str.find_first_not_of(" "));
	str.erase(str.find_last_not_of(" ") + 1);
	return str;
}
//获取请求命令与参数
bool GetReqeustCommandAndParmeter(std::string strUri, std::string & strRequestOperateCommand, std::list<NameAndValue> & listRequestOperateParameter)
{
	bool bRet = false;
	std::vector<std::string> vecRequest;
	int nRetSplit = StringSplit(vecRequest, strUri, "?");
	if (nRetSplit > 0)
	{
		if (vecRequest.size() == 1)
		{
			strRequestOperateCommand = vecRequest[0];
		}
		else if (vecRequest.size() > 1)
		{
			strRequestOperateCommand = vecRequest[0];
			std::string strRequestParameter = vecRequest[1];
			std::vector<std::string> vecParams;
			nRetSplit = StringSplit(vecParams, strRequestParameter, "&");
			if (nRetSplit > 0)
			{
				std::vector<std::string>::iterator iter, iterEnd;
				iter = vecParams.begin();
				iterEnd = vecParams.end();
				for (iter; iter != iterEnd; iter++)
				{
					std::vector<std::string> vecNameOrValue;
					nRetSplit = StringSplit(vecNameOrValue, *iter, "=");
					if (nRetSplit > 0)
					{
						NameAndValue nvNameAndValue;
						nvNameAndValue.strName = vecNameOrValue[0];
						nvNameAndValue.strValue = "";
						if (vecNameOrValue.size() > 1)
						{
							nvNameAndValue.strValue = vecNameOrValue[1];
						}
						//insert
						listRequestOperateParameter.push_back(nvNameAndValue);
					}
				}
			}
		}
		else
		{

		}
	}
	return bRet;
}